﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Drawing;
using System.Runtime.InteropServices;

namespace GeoPlugin
{
    class ControlProcessor
    {
        [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SetCursorPos")]
        [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
        public static extern bool SetCursorPos(int X, int Y);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);

        [Flags]
        public enum MouseEventFlags : uint
        {
            LEFTDOWN = 0x00000002,
            LEFTUP = 0x00000004,
            MIDDLEDOWN = 0x00000020,
            MIDDLEUP = 0x00000040,
            MOVE = 0x00000001,
            ABSOLUTE = 0x00008000,
            RIGHTDOWN = 0x00000008,
            RIGHTUP = 0x00000010
        }

        private bool runThread;
        private bool newResultFromMarkerProcessor = false;
        private ObjectControl objectControl;
        private int imageWidth;
        private List<Spat> markerPositions;

        public int ImageWidth
        {
            get { return imageWidth; }
            set { imageWidth = value; }
        }
        private int imageHeight;

        public int ImageHeight
        {
            get { return imageHeight; }
            set { imageHeight = value; }
        }

        public ObjectControl ObjectControl
        {
            get { return objectControl; }
            set { objectControl = value; }
        }

        public bool NewResultFromMarkerProcessor
        {
            get { return newResultFromMarkerProcessor; }
            set { newResultFromMarkerProcessor = value; }
        }

        public bool RunThread
        {
            get { return runThread; }
            set { runThread = value; }
        }

        public ControlProcessor()
        { 
        
        }
        private static int zoomOutCount = 0;
        private static int previousFirstMarkerXout = 0;
        private static int previousFirstMarkerYout = 0;
        private static int previousSecondMarkerXout = 0;
        private static int previousSecondMarkerYout = 0;

        private double ratio;

        public double Ratio
        {
            get { return ratio; }
            set { ratio = value; }
        }

        private void CheckForZoomOut()
        {
            if (markerPositions[0].getCenter() != null && markerPositions[1].getCenter() != null)
            {

                Point? firstMarker = markerPositions[0].getCenter();
                Point? secondMarker = markerPositions[1].getCenter();
                if ((secondMarker.Value.X - firstMarker.Value.X) > ImageWidth / 10 &&
                    (secondMarker.Value.Y - firstMarker.Value.Y) > ImageHeight / 10)
                {
                    //Console.WriteLine(DateTime.Now.Second + " Markers are in a good position!");
                    zoomOutCount++;
                    if (zoomOutCount == 1)
                    {
                        previousFirstMarkerXout = firstMarker.Value.X;
                        previousFirstMarkerYout = firstMarker.Value.Y;
                        previousSecondMarkerXout = secondMarker.Value.X;
                        previousSecondMarkerYout = secondMarker.Value.Y;
                    }
                    if (zoomOutCount > 15)
                    {
                        if (((previousFirstMarkerXout - firstMarker.Value.X) > ImageWidth / 10) &&
                            ((previousFirstMarkerYout - firstMarker.Value.Y) > ImageHeight / 10) &&
                            ((previousSecondMarkerXout - secondMarker.Value.X) < -ImageWidth / 10) &&
                            ((previousSecondMarkerYout - secondMarker.Value.Y) < -ImageHeight / 10))
                        {
                            Console.WriteLine(DateTime.Now.Ticks + " Do zoom in!");
                            ObjectControl.Dispatcher.BeginInvoke(new Action(delegate()
                            {
                                ObjectControl.zoomSlider.Value -= 15;
                                ObjectControl.zoomSlider.RaiseEvent(new System.Windows.RoutedEventArgs(System.Windows.Controls.Slider.GotFocusEvent));
                            }));

                        }
                        zoomOutCount = 0;
                    }

                    //Console.WriteLine("Zoom count: " + zoomCount);
                }
            }
            else
            {
                zoomOutCount = 0;
            }
        }

        private static int zoomInCount = 0;
        private static int previousFirstMarkerXin = 0;
        private static int previousFirstMarkerYin = 0;
        private static int previousSecondMarkerXin = 0;
        private static int previousSecondMarkerYin = 0;

        private void CheckForZoomIn()
        {
            if (markerPositions[0].getCenter() != null && markerPositions[1].getCenter() != null)
            {

                Point? firstMarker = markerPositions[0].getCenter();
                Point? secondMarker = markerPositions[1].getCenter();
                if ((secondMarker.Value.X - firstMarker.Value.X) > ImageWidth / 5 &&
                    (secondMarker.Value.Y - firstMarker.Value.Y) > ImageHeight / 5)
                {
                    //Console.WriteLine(DateTime.Now.Second + " Markers are in a good position!");
                    zoomInCount++;
                    if (zoomInCount == 1)
                    {
                        previousFirstMarkerXin = firstMarker.Value.X;
                        previousFirstMarkerYin = firstMarker.Value.Y;
                        previousSecondMarkerXin = secondMarker.Value.X;
                        previousSecondMarkerYin = secondMarker.Value.Y;
                    }
                    if (zoomInCount > 15)
                    {
                        if (((previousFirstMarkerXin - firstMarker.Value.X) < -ImageWidth / 10) &&
                            ((previousFirstMarkerYin - firstMarker.Value.Y) < -ImageHeight / 10) &&
                            ((previousSecondMarkerXin - secondMarker.Value.X) > ImageWidth / 10) &&
                            ((previousSecondMarkerYin - secondMarker.Value.Y) > ImageHeight / 10))
                        {
                            Console.WriteLine(DateTime.Now.Ticks + " Do zoom out!");
                            ObjectControl.Dispatcher.BeginInvoke(new Action(delegate()
                            {
                                ObjectControl.zoomSlider.Value += 15;
                                ObjectControl.zoomSlider.RaiseEvent(new System.Windows.RoutedEventArgs(System.Windows.Controls.Slider.GotFocusEvent));
                            }));

                        }
                        zoomInCount = 0;
                    }

                    //Console.WriteLine("Zoom count: " + zoomCount);
                }
            }
            else
            {
                zoomInCount = 0;
            }
        }

        private bool markerIsOut = true;
        private bool rotateStarted = false;
        private void CheckForRotate()
        {
            if (markerPositions[0].getCenter() == null && markerPositions[1].getCenter() != null)
            {
                Point? secondMarker = markerPositions[1].getCenter();
                if (markerIsOut && !rotateStarted)
                {
                    ObjectControl.Dispatcher.BeginInvoke(new Action(delegate()
                    {
                        ObjectControl.rotateRectangle.Tag = new System.Windows.Point(secondMarker.Value.X, secondMarker.Value.Y);
                        ObjectControl.rotateRectangle.RaiseEvent(new System.Windows.RoutedEventArgs(System.Windows.FrameworkElement.GotFocusEvent));
                    }));
                    markerIsOut = false;
                    rotateStarted = true;
                }
                if (rotateStarted)
                {
                    ObjectControl.Dispatcher.BeginInvoke(new Action(delegate()
                    {
                        ObjectControl.rotateRectangle.Tag = new System.Windows.Point(secondMarker.Value.X, secondMarker.Value.Y);
                        ObjectControl.rotateRectangle.RaiseEvent(new System.Windows.RoutedEventArgs(System.Windows.FrameworkElement.UnloadedEvent));
                    }));
                }

            }
            else
            {
                if (!markerIsOut && rotateStarted)
                {
                    ObjectControl.Dispatcher.BeginInvoke(new Action(delegate()
                    {
                        ObjectControl.rotateRectangle.Tag = new System.Windows.Point(0, 0);
                        ObjectControl.rotateRectangle.RaiseEvent(new System.Windows.RoutedEventArgs(System.Windows.FrameworkElement.LostFocusEvent));
                    }));
                }
                rotateStarted = false;
                markerIsOut = true;
            }
        }

        public void ThreadRun()
        {
            try
            {
                Thread.Sleep(2000);
                double height = this.ObjectControl.TestImage.ActualHeight;
                this.Ratio = height / this.ImageHeight;
                int nrOfMarkers = PluginModel.Instance.MarkerPositions.Count;
                double[, ,] distances = new double[nrOfMarkers, nrOfMarkers, 10];
                int index = 0;
                bool breaker = false;
                int nrOfFrames = 0;
                bool mouseDown = false;
                while (RunThread) 
                {
                    if (index == 9) 
                    {
                        index = 0;
                    }
                    List<Spat> markers = new List<Spat>(PluginModel.Instance.MarkerPositions);
                    for (int i = 0; i < markers.Count; i++)
                    {
                        for (int j = 0; j < markers.Count; j++)
                        {
                            if (i != j)
                            {
                                try
                                {
                                    System.Drawing.Point marker1 = (System.Drawing.Point)markers[i].getCenter();
                                    System.Drawing.Point marker2 = (System.Drawing.Point)markers[j].getCenter();
                                    distances[i, j, index] = CalculateDistance(marker1, marker2);
                                    double maximum;
                                    double averageDistance = CalculateSumAndMaximum(distances, i, j, out maximum) / 10;
                                    float ratio = (float)(averageDistance / maximum);
                                    if ((averageDistance < 40) && (ratio > 0.50) && (ratio < 1.3) && (averageDistance > 10))
                                    {
                                        nrOfFrames++;
                                        if (nrOfFrames > 30 && !mouseDown) 
                                        {
                                            mouseDown = true;
                                            System.Drawing.Point clickLocation = this.CalculateMidpoint(marker1, marker2);
                                            this.ColorMarkerClick(clickLocation, 1);
                                            breaker = true;
                                            Thread.Sleep(40);
                                            break;
                                        }
                                        if (mouseDown) 
                                        {
                                            System.Drawing.Point clickLocation = this.CalculateMidpoint(marker1, marker2);
                                            this.ColorMarkerClick(clickLocation, 3);
                                            breaker = true;
                                            Thread.Sleep(40);
                                            break;
                                        }
                                    }
                                    else 
                                    {
                                        if (nrOfFrames > 10 && nrOfFrames < 30) 
                                        {
                                            System.Drawing.Point clickLocation = this.CalculateMidpoint(marker1, marker2);
                                            this.ColorMarkerClick(clickLocation, 0);
                                            breaker = true;
                                            nrOfFrames = 0;
                                            distances = new double[nrOfMarkers, nrOfMarkers, 10];
                                            Thread.Sleep(40);
                                            break;
                                        }

                                        if (mouseDown) 
                                        {
                                            System.Drawing.Point clickLocation = this.CalculateMidpoint(marker1, marker2);
                                            this.ColorMarkerClick(clickLocation, 2);
                                            mouseDown = false;
                                            breaker = true;
                                            nrOfFrames = 0;
                                            distances = new double[nrOfMarkers, nrOfMarkers, 10];
                                            Thread.Sleep(40);
                                            break;
                                        }
                                        
                                    }
                                    Console.WriteLine("Marker {0} , Marker {1} \t Average distance: {2} , Maximum = {3} , Ration = {4} ", i, j, averageDistance, maximum, ratio);
                                }
                                catch (InvalidCastException ex)
                                {
                                    distances[i, j, index] = 0;
                                }
                                catch (InvalidOperationException ex)
                                {
                                    distances[i, j, index] = 0;
                                }
                            }
                        }
                        if (breaker) 
                        {
                            breaker = false;
                            break;
                        }
                    }
                    index++;
                    Thread.Sleep(40);
                }
            }
            catch (System.OperationCanceledException ex)
            {

            }
            finally 
            { 
            
            }
        }

        private double CalculateSumAndMaximum(double[, ,] matrix, int marker1, int marker2, out double maximum)
        {
            maximum = -1;
            double sum = 0;
            for (int i = 0; i < 10; i++)
            {
                double currentDistance = matrix[marker1, marker2, i];
                if (maximum < currentDistance)
                    maximum = currentDistance;
                sum = sum + currentDistance;
            }
            if (maximum == 0)
            {
                maximum = -1;
            }
            return sum;
        }
        private double CalculateDistance(System.Drawing.Point p1, System.Drawing.Point p2)
        {
            if (p1 == null || p2 == null)
                return -1;
            return Math.Sqrt((p2.X - p1.X) * (p2.X - p1.X) + (p2.Y - p1.Y) * (p2.Y - p1.Y));
        }

        private System.Drawing.Point CalculateMidpoint(System.Drawing.Point p1, System.Drawing.Point p2)
        {
            return new System.Drawing.Point((int)((p1.X + p2.X) / 2), (int)((p1.Y + p2.Y) / 2));
        }

        private delegate System.Windows.Point myDelegate(System.Windows.Point p);

        private void ColorMarkerClick(System.Drawing.Point clickLocation, short action)
        {
            //throw new NotImplementedException();
            System.Windows.Point point = new System.Windows.Point(clickLocation.X * this.Ratio, clickLocation.Y * this.Ratio);

            System.Windows.Threading.DispatcherOperation operation =
            this.ObjectControl.Dispatcher.BeginInvoke(Delegate.CreateDelegate(typeof(myDelegate), this.ObjectControl.TestImage, "PointToScreen"), System.Windows.Threading.DispatcherPriority.Send, point);
            switch (action)
            { 
                case 0:
                    operation.Completed += new EventHandler(operation_Completed);
                    break;
                case 1:
                    operation.Completed += new EventHandler(operation_MouseDownCompleted);
                    break;
                case 2:
                    operation.Completed += new EventHandler(operation_MouseUpCompleted);
                    break;
                case 3:
                    operation.Completed += new EventHandler(operation_MouseMove);
                    break;

            }

        }


        private void operation_Completed(object sender, EventArgs e)
        {
            System.Windows.Threading.DispatcherOperation operation = (System.Windows.Threading.DispatcherOperation)sender;
            System.Windows.Point point = (System.Windows.Point)operation.Result;
            SetCursorPos(Convert.ToInt32(point.X), Convert.ToInt32(point.Y));
            mouse_event((uint)(MouseEventFlags.LEFTDOWN | MouseEventFlags.ABSOLUTE), (uint)point.X, (uint)point.Y, 0, UIntPtr.Zero);
            mouse_event((uint)(MouseEventFlags.LEFTUP | MouseEventFlags.ABSOLUTE), (uint)point.X, (uint)point.Y, 0, UIntPtr.Zero);
        }

        private void operation_MouseDownCompleted(object sender, EventArgs e)
        {
            System.Windows.Threading.DispatcherOperation operation = (System.Windows.Threading.DispatcherOperation)sender;
            System.Windows.Point point = (System.Windows.Point)operation.Result;
            SetCursorPos(Convert.ToInt32(point.X), Convert.ToInt32(point.Y));
            mouse_event((uint)(MouseEventFlags.RIGHTDOWN | MouseEventFlags.ABSOLUTE), (uint)point.X, (uint)point.Y, 0, UIntPtr.Zero);
        }

        private void operation_MouseUpCompleted(object sender, EventArgs e)
        {
            System.Windows.Threading.DispatcherOperation operation = (System.Windows.Threading.DispatcherOperation)sender;
            System.Windows.Point point = (System.Windows.Point)operation.Result;
            SetCursorPos(Convert.ToInt32(point.X), Convert.ToInt32(point.Y));
            mouse_event((uint)(MouseEventFlags.RIGHTUP | MouseEventFlags.ABSOLUTE), (uint)point.X, (uint)point.Y, 0, UIntPtr.Zero);
        }

        private void operation_MouseMove(object sender, EventArgs e)
        {
            System.Windows.Threading.DispatcherOperation operation = (System.Windows.Threading.DispatcherOperation)sender;
            System.Windows.Point point = (System.Windows.Point)operation.Result;
            SetCursorPos(Convert.ToInt32(point.X), Convert.ToInt32(point.Y));
            //mouse_event((uint)(MouseEventFlags.MOVE | MouseEventFlags.ABSOLUTE), (uint)point.X, (uint)point.Y, 0, UIntPtr.Zero);
        }
    }
}
